﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GodPl.Parser;
using GodPl.Exceptions;
using GodPl.WorldState;
using GodPl.Utilities;
using GodPl.GameObjects.Creatures;
using GodPl.GameObjects.Creatures.Actions;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace GodPl.GameObjects.Creatures
{
    /// <summary>
    /// Main class for the Briyyot creatures.
    /// </summary>
    /// <remarks> In the future this may be refactored to support other creatures.</remarks>
    public partial class Briyya : GameObject, IObstacle, IHittable
    {

        int id;

        public int Id
        {
            get { return id; }
        }


        public override Vector2 Position
        {
            get
            {
                return base.Position;
            }
            set
            {
                value.X = MathHelper.Clamp(value.X, 0, GodPlGame.ScreenWidth);
                value.Y = MathHelper.Clamp(value.Y, 0, GodPlGame.ScreenHeight);

                base.Position = value;
            }
        }

        /// <summary>
        /// How long the Briyya has been alive
        /// </summary>
        TimeSpan lifeTime;

        List<TimeSpan> driveTimers; 

        /// <summary>
        /// The Briyya's current stage of life
        /// </summary>
        LifeStage lifeStage;

        /// <summary>
        /// Briyya's set of genes
        /// </summary>
        Genes genes;

        /// <summary>
        /// Briyya's physiological and psychological drives
        /// </summary>
        Drives drives;


        public LifeStage LifeStage
        {
            get { return lifeStage; }
        }

        public Genes Genes
        {
            get { return genes; }
        }

        public Drives Drives
        {
            get { return drives; }
        }

        Texture2D sprite;
        Color color;
        float rotation;

        float speedScale;

        Vector2 velocity;

        TimeSpan ChildStageLength;
        TimeSpan AdultStageLength;

        IBriyyaUpdater userUpdater = null;

        public IBriyyaUpdater UserUpdater
        {
            get { return userUpdater; }
            set
            {
                userUpdater = value;
                if (userUpdater != null)
                {
                    userUpdater.SetActor(this);
                }
            }
        }

        public Vector2 Velocity
        {
            get { return velocity; }
            set
            { 
                if (float.IsNaN(value.X) || float.IsNaN(value.Y))
                {
                    throw new ApplicationException("Briyya " + this.Id + ": Assigning NaN velocity");
                }
                
                velocity = value;
                if (value.Length() > this.MaxSpeed)
                {
                    value.Normalize();
                    velocity = value * this.MaxSpeed;
                }

                

            }
        }

        public float Rotation
        {
            get { return rotation; }
            set { rotation = value % (2 * (float)Math.PI); }
        }

        public float MaxSpeed
        {
            get { return genes[GeneName.Swiftness] * speedScale * Briyya.MaxBriyyaSpeed; }
        }

        /// <summary>
        /// Whether or not the Briyya is currently selected. (Used for highlighting it on-screen.)
        /// </summary>
        public bool Selected { get; set; }

        /// <summary>
        /// The Briyya's sex
        /// </summary>
        public Sex Sex
        {
            get { return genes.Sex; }
        }

        BriyyaAction currentAction;

        public BriyyaAction CurrentAction
        {
            get { return currentAction; }
            protected set
            {
                if (currentAction == null || currentAction.Done || currentAction.Interrupt())
                {
                    currentAction = value;
                }
                else
                {
                    //throw new BusyBriyyaException(currentAction);
                }
            }
        }

        #region Constructors & Initializers

        public Briyya(World world, Vector2 position, Genes genes)
            : base(world, position)
        {
            

            

            driveTimers = new List<TimeSpan>();
            for (int i = 0; i < Enum.GetValues(typeof(DriveName)).Length; ++i)
            {
                driveTimers.Add(TimeSpan.Zero);
            }

            this.genes = genes;
            genes.Briyya = this;
            this.id = world.NextBriyyaId();

            drives = new Drives(this);

            ChildStageLength = Briyya.MaxLifeTime.Times(Genes[GeneName.Longevity] * 0.2f);
            AdultStageLength = Briyya.MaxLifeTime.Times(Genes[GeneName.Longevity] * 0.6f);

            StartLifeStage(LifeStage.Child);

            LoadContent();
            color = new Color(genes[GeneName.ColorRed], genes[GeneName.ColorGreen], genes[GeneName.ColorBlue]);
            localBounds.Width = (int)Math.Round(sprite.Width * spriteScale);
            localBounds.Height = (int)Math.Round(sprite.Height * spriteScale);

            this.UserUpdater = GodPlCompiler.GetNewInstance();
            if (this.UserUpdater == null)
            {
                this.UserUpdater = new Behaviors.StandardBriyyaBehavior();
            }
        }

        public override void LoadContent()
        {
            if (Sex == Sex.Male)
            {
                sprite = World.Content.Load<Texture2D>("Sprites/Briyya-Gray-150");
            }
            else
            {
                sprite = World.Content.Load<Texture2D>("Sprites/Briyya-Female-Gray-150");
            }
        }

        #endregion

        
        #region LifeStages

        /// <summary>
        /// Performs changes required when a creature advances in lifestage
        /// </summary>
        /// <param name="stage"></param>
        public void StartLifeStage(LifeStage stage)
        {
            this.lifeStage = stage;
            switch (stage)
            {
                case LifeStage.Child:

                    lifeTime = new TimeSpan();
                    spriteScale = 0.5f * genes[GeneName.SizeScale];
                    speedScale = 0.5f;

                    break;

                case LifeStage.Adult:

                    spriteScale = 0.8f * genes[GeneName.SizeScale];
                    speedScale = 0.8f;

                    break;

                case LifeStage.Senior:

                    spriteScale = 0.7f * genes[GeneName.SizeScale];
                    speedScale = 0.6f;

                    drives[DriveName.SexDrive] = 0;

                    break;

                case LifeStage.Dead:
                    localBounds = new Rectangle();

                    drives[DriveName.Boredom] = 0;
                    drives[DriveName.Fear] = 0;
                    drives[DriveName.Hunger] = 0;
                    drives[DriveName.Pain] = 0;
                    drives[DriveName.SexDrive] = 0;
                    drives[DriveName.Tiredness] = 0;

                    World.DeleteObject(this);

                    break;

            }

        }

        #endregion

        #region Drives


        #endregion




        public override void Update(GameTime gameTime)
        {
            // Update life
            lifeTime += gameTime.ElapsedGameTime;
            if (lifeStage == LifeStage.Child && lifeTime >= ChildStageLength)
            {
                GodPlGame.LogLine("Briyya " + this.Id + " became an adult.");
                StartLifeStage(LifeStage.Adult);
            }
            else if (lifeStage == LifeStage.Adult && lifeTime >= (ChildStageLength + AdultStageLength))
            {
                GodPlGame.LogLine("Briyya " + this.Id + " became a senior.");
                StartLifeStage(LifeStage.Senior);
            }
            else if (lifeStage == LifeStage.Senior && lifeTime >= Briyya.MaxLifeTime.Times(Genes[GeneName.Longevity]))
            {
                GodPlGame.LogLine("Briyya " + this.Id + " died of old age.");
                StartLifeStage(LifeStage.Dead);
            }


            if (lifeStage == LifeStage.Dead)
            {
                return;
            }

            if (currentAction != null)
            {
                currentAction.Update(gameTime);
            }

            drives[DriveName.Tiredness] += TirednessRate;
            drives[DriveName.Hunger] += HungerRate;

            if (this.lifeStage == LifeStage.Adult)
            {
                drives[DriveName.SexDrive] += SexDriveRate * genes[GeneName.Libido];
            }


            foreach (DriveName drive in Enum.GetValues(typeof(DriveName)))
            {
                if (this.Drives[drive] > 0.95f)
                {
                    driveTimers[(int)drive] += gameTime.ElapsedGameTime;
                }
                else
                {
                    driveTimers[(int)drive] = TimeSpan.Zero;
                }

            }


            if (driveTimers[(int)DriveName.Hunger].TotalSeconds > 5)
            {
                GodPlGame.LogLine("Briyya " + this.Id + " died of hunger.");
                StartLifeStage(LifeStage.Dead);
            }


            if (driveTimers[(int)DriveName.Tiredness].TotalSeconds > 10)
            {
                GodPlGame.LogLine("Briyya " + this.Id + " died of exhaustion.");
                StartLifeStage(LifeStage.Dead);
            }

            if (userUpdater != null)
            {
                userUpdater.Update(gameTime);
            }

            UpdateMotion(gameTime);

        }

        private void UpdateMotion(GameTime gameTime)
        {
            // Hack to fix bug in which the Briyya goes flying off in one direction
            AMoveTowards moveAction = this.currentAction as AMoveTowards;

            if (moveAction == null || moveAction.Running == false)
            {
                this.Velocity = Vector2.Zero;
            }
            
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
            position += Velocity * elapsed;

            if (float.IsNaN(position.X) || float.IsNaN(position.Y))
            {
                throw new ApplicationException("Briyya " + this.Id + ": NaN position!!!");
            }
        }

        public override void Draw(GameTime gameTime, Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch)
        {
            Color renderColor = color;

            if (Selected)
            {
                Vector3 ones = new Vector3(1f,1f,1f);
                var v1 = color.ToVector3();
                var v2 = Vector3.Multiply(v1, 1f + (float)Math.Sin((double)gameTime.TotalGameTime.TotalMilliseconds / 200) / 10f);
                renderColor = new Color(v2);
            }

            if (lifeStage == LifeStage.Dead)
            {
                renderColor.A = 100;
            }

            spriteBatch.Draw(sprite, Position, null, renderColor, rotation, new Vector2(sprite.Width / 2, sprite.Height / 2), spriteScale, SpriteEffects.None, 0f);

        }


        public void MoveTowards(GameObject target)
        {
            try
            {
                this.CurrentAction = new AMoveTowards(this, target);
                this.CurrentAction.Begin();
            }
            catch (GameLogicException e)
            {
                GodPlGame.LogLine(e.Message);
            }
        }


        public void EatFrom(IFoodSource target)
        {
            try
            {
                this.CurrentAction = new AEatFrom(this, target);
                this.CurrentAction.Begin();
            }
            catch (GameLogicException e)
            {
                GodPlGame.LogLine(e.Message);
            }
        }

        public void BreedWith(Briyya target)
        {
            try
            {
                this.CurrentAction = new ABreedWith(this, target);
                this.CurrentAction.Begin();
            }
            catch (GameLogicException e)
            {
                GodPlGame.LogLine(e.Message);
            }
        }

        public void Hit(IHittable target)
        {

        }

        public void Sleep()
        {
            try
            {
                this.CurrentAction = new ASleep(this);
                this.CurrentAction.Begin();
            }
            catch (GameLogicException e)
            {
                GodPlGame.LogLine(e.Message);
            }
        }

        public void BeBredWith()
        {
            this.Drives[DriveName.SexDrive] -= 0.75f;
        }

        public override string ToString()
        {
            string s = "{ ";
            s += this.Id + "\r\n ";
            s += Enum.GetName(typeof(LifeStage), this.lifeStage) + "\r\n ";
            s += Enum.GetName(typeof(Sex), this.Sex) + "\r\n ";

            if (this.CurrentAction != null)
            {
                s += this.CurrentAction.GetType().ToString() + "\r\n ";
            }
            else
            {
                s += "Idle \r\n ";
            }

            s += this.Drives.ToString() + "\r\n ";
            s += this.color.ToString() + "\r\n ";
            s += this.Genes.ToString() + "\r\n ";

            s += " }";
            return s;
        }



        #region IObstacle Members

        public ObstacleGravityType GravityType
        {
            get { return ObstacleGravityType.Point; }
        }

        #endregion

        #region IHittable Members

        public void BeHit(Briyya actor, float intensity)
        {
            throw new NotImplementedException();
        }

        #endregion
    }


    

}
